TerraformでAWS Storage Gateway (S3 File Gateway)を作ってみた
しばたです。
先日検証用にStorage Gateway (S3 File Gateway)を作る必要があり、どう構築しようか悩んでいたところTerraformがStorage Gatewayに対応していたので試してみることにしました。
構築環境について
今回作った環境は既存VPCのPrivate SubnetにEC2を構築、Storage GatewayはS3 File GatewayでSMBアクセス可能とするものとなります。
検証用なので認証はゲストユーザーとしています。
図としてはこんな感じです。
(Storage Gateway APIエンドポイントへのアクセスのためPrivate Subnetからインターネット通信可能にしています)
構築用tfファイルはGistにアップロードしています。
一応本記事にも転記しておきます。展開してご覧ください。
data "aws_caller_identity" "current" {}
// System name settings
variable "sysname" {
type = string
default = "mysgw"
}
variable "envname" {
type = string
default = "dev"
}
// VPC settings
data "aws_vpc" "vpc" {
// Set your VPC id
id = "vpc-1234567890"
}
data "aws_subnet" "sgw_subnet" {
// Set your Storage Gateway subnet id
id = "subnet-1234567890"
}
// Other settings
locals {
// Set your EC2 key pair name
ec2_keyname = "my-keypair"
}
provider "aws" {
region = "ap-northeast-1"
}
terraform {
backend "local" {
}
}
//
// Storage Gatewayの構築に必要な前提リソース
//
//
// S3
//
resource "aws_s3_bucket" "sgw" {
bucket = "${var.sysname}-${var.envname}-storage-gateway-${data.aws_caller_identity.current.account_id}"
acl = "private"
}
resource "aws_s3_bucket_public_access_block" "sgw" {
bucket = aws_s3_bucket.sgw.bucket
block_public_acls = true
block_public_policy = true
ignore_public_acls = true
restrict_public_buckets = true
}
//
// Security Group
//
resource "aws_security_group" "sgw" {
vpc_id = data.aws_vpc.vpc.id
name = "${var.sysname}-${var.envname}-storage-gateway-sg"
description = "Security group for Storage Gateway"
// HTTP for activation
ingress {
from_port = 80
to_port = 80
protocol = "tcp"
description = "HTTP for activation"
cidr_blocks = [data.aws_vpc.vpc.cidr_block]
}
// SMB
ingress {
from_port = 445
to_port = 445
protocol = "tcp"
description = "SMB"
cidr_blocks = [data.aws_vpc.vpc.cidr_block]
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
tags = {
Name = "${var.sysname}-${var.envname}-storage-gateway-sg"
}
}
//
// EC2
//
data "aws_ssm_parameter" "sgw_ami" {
name = "/aws/service/storagegateway/ami/FILE_S3/latest"
}
resource "aws_instance" "sgw" {
instance_type = "m5.xlarge" // AWS推奨スペック
ami = nonsensitive(data.aws_ssm_parameter.sgw_ami.value)
subnet_id = data.aws_subnet.sgw_subnet.id
key_name = local.ec2_keyname
vpc_security_group_ids = [aws_security_group.sgw.id]
iam_instance_profile = "" // Instance profileなし
disable_api_termination = false // 検証用なので削除可
associate_public_ip_address = false // Private subnetに配備
root_block_device {
volume_type = "gp3"
iops = 3000 // default value
throughput = 125 // default value
volume_size = 80 // AMIのデフォルト値
delete_on_termination = true
encrypted = false
tags = {
Name = "${var.sysname}-${var.envname}-storage-gateawy-root"
}
}
ebs_block_device {
// キャッシュ用EBS
device_name = "/dev/sdf"
volume_type = "gp3"
iops = 3000 // default value
throughput = 125 // default value
volume_size = 150 // 最低容量
delete_on_termination = true
encrypted = false
tags = {
Name = "${var.sysname}-${var.envname}-storage-gateawy-cache"
}
}
tags = {
Name = "${var.sysname}-${var.envname}-storage-gateway"
}
lifecycle {
ignore_changes = [associate_public_ip_address]
}
}
//
// Storage Gateway関連のリソース
//
//
// IAM role for Storage Gateway file share
//
data "aws_iam_policy_document" "sgw_fileshare" {
statement {
actions = ["sts:AssumeRole"]
principals {
type = "Service"
identifiers = ["storagegateway.amazonaws.com"]
}
}
}
resource "aws_iam_policy" "sgw_fileshare" {
name = "${var.sysname}-${var.envname}-storage-gateawy-fileshare"
policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Action = [
"s3:GetAccelerateConfiguration",
"s3:GetBucketLocation",
"s3:GetBucketVersioning",
"s3:ListBucket",
"s3:ListBucketVersions",
"s3:ListBucketMultipartUploads"
]
Effect = "Allow"
Resource = "${aws_s3_bucket.sgw.arn}"
},
{
Action = [
"s3:AbortMultipartUpload",
"s3:DeleteObject",
"s3:DeleteObjectVersion",
"s3:GetObject",
"s3:GetObjectAcl",
"s3:GetObjectVersion",
"s3:ListMultipartUploadParts",
"s3:PutObject",
"s3:PutObjectAcl"
]
Effect = "Allow"
Resource = "${aws_s3_bucket.sgw.arn}/*"
}
]
})
}
resource "aws_iam_role" "sgw_fileshare" {
name = "${var.sysname}-${var.envname}-storage-gateawy-fileshare"
assume_role_policy = data.aws_iam_policy_document.sgw_fileshare.json
managed_policy_arns = [
aws_iam_policy.sgw_fileshare.arn
]
}
//
// Storage Gateway
// ※ リソース作成後も以下のパラメーターは手動で設定する必要有り
// - CloudWatch Logs設定
// - メンテナンス時間
resource "aws_storagegateway_gateway" "sgw" {
// gateway_ip_address = aws_instance.sgw.private_ip
// 今回はTerraformから直接EC2へアクセスできないため事前にAtivation Keyを取得している
activation_key = "XXXXX-XXXXX-XXXXX-XXXXX-XXXXX"
gateway_name = "${var.sysname}-${var.envname}-gateway"
gateway_timezone = "GMT+9:00" // JST
gateway_type = "FILE_S3" // S3 File Storage
smb_guest_password = "P@ssw0rd" // ゲストユーザーパスワード
lifecycle {
ignore_changes = [smb_guest_password]
}
}
// Local Cache 設定
data "aws_storagegateway_local_disk" "sgw" {
gateway_arn = aws_storagegateway_gateway.sgw.arn
disk_node = "/dev/sdf"
}
resource "aws_storagegateway_cache" "sgw" {
gateway_arn = aws_storagegateway_gateway.sgw.arn
disk_id = data.aws_storagegateway_local_disk.sgw.id
}
//
// SMB File share
//
resource "aws_storagegateway_smb_file_share" "sgw" {
authentication = "GuestAccess"
gateway_arn = aws_storagegateway_gateway.sgw.arn
location_arn = aws_s3_bucket.sgw.arn
role_arn = aws_iam_role.sgw_fileshare.arn
file_share_name = "share" // 今回はFSx for Windowsと同様に共有名を share としている
object_acl = "bucket-owner-full-control"
}
Terraformのバージョンは
- Terraform 1.0.5
- Terraform AWS provider 3.58.0
で動作確認しています。
Storage Gatewayの基本と構築の限界
EC2でStorage Gatewayを構築する場合専用のAMIからEC2を構築しアクティベーションしてやる必要があります。
これが曲者で構築したEC2にHTTPアクセスしないとアクティベーションできません。
EC2がパブリックに公開されTerraformの実行環境から直接アクセス可能であれば一気通貫で環境構築ができるのですが、そうでない場合
- Storage Gateway用EC2を構築
- 構築したEC2にHTTPアクセスし、専用の「アクティベーションコード」を取得
- 「アクティベーションコード」を使いStorage Gatewayを構築
と段階を分けて作業してやる必要があります。
このため今回のtfファイルもstorage-gateway-01.tf
、storage-gateway-02.tf
と2つに分割しています。
構築手順
ここから実際に環境構築手順を解説します。
1. 前準備 (EC2, S3の構築)
前節で述べた通りPrivate SubnetにStorage Gatewayを構築する場合アクティベーションキーを取得するため二段階に分けて作業を行います。
まずはstorage-gateway-02.tf
をリネームするなどして最初にstorage-gateway-01.tf
だけを実行してください。
これにより
- S3 (
<system name>-<environment name>-storage-gateway-<account id>
) - EC2 (
<system name>-<environment name>-storage-gateway
) - セキュリティグループ (
<system name>-<environment name>-storage-gateway
)
が作成されます。
S3の設計に関して特別なことはしていません。
EC2にはStorage Gateway用のAMIが用意されているため、SSM Parameterから最新のAMI IDを取得しています。
// SSM Parameterから最新のStorage Gateway AMIを取得
data "aws_ssm_parameter" "sgw_ami" {
name = "/aws/service/storagegateway/ami/FILE_S3/latest"
}
またインスタンスタイプについて、ドキュメントではm4.xlarge
以上を推奨しているため現行世代のm5.xlarge
としています。
加えてルートボリューム(要80GiB)に加えて必ずキャッシュ用ボリュームを用意する必要があります。
今回はgp3で最低容量の150GiBとしています。
この辺のスペックに関しては必要に応じて変更すると良いでしょう。
セキュリティグループはシンプルにVPC CIDRの範囲でHTTPとSMBを公開しています。
これからStorage Gatewayを構築するにはアクティベーションキーを取得してやる必要があります。
構築したEC2にHTTPアクセス可能な環境(今回は既存Bastion)からドキュメントの手順に従ってください。
今回はWindows ServerのBastionを使っているのでPowerShellを使い以下の様にして取得しています。
function Get-ActivationKey {
[CmdletBinding()]
Param(
[parameter(Mandatory=$true)][string]$IpAddress,
[parameter(Mandatory=$true)][string]$ActivationRegion
)
PROCESS {
$request = Invoke-WebRequest -UseBasicParsing -Uri "http://$IpAddress/?activationRegion=$ActivationRegion" -MaximumRedirection 0 -ErrorAction SilentlyContinue
if ($request) {
$activationKeyParam = $request.Headers.Location | Select-String -Pattern "activationKey=([A-Z0-9-]+)"
$activationKeyParam.Matches.Value.Split("=")[1]
}
}
}
Get-ActivationKey -IpAddress 10.0.21.125 -ActivationRegion ap-northeast-1
25桁のキー(今回はQNI59-SJ9DE-XXXXX-XXXXX-XXXXX
)を取得をできればOKです。
2. Storage Gatewayの構築
アクティベーションキーが取得できたのでstorage-gateway-02.tf
に転記して実行します。
resource "aws_storagegateway_gateway" "sgw" {
// 今回はTerraformから直接EC2へアクセスできないため事前にAtivation Keyを取得している
activation_key = "QNI59-SJ9DE-XXXXX-XXXXX-XXXXX"
// snip...
}
これで
- ファイル共有用 IAM Role (
<system name>-<environment name>-storage-gateawy-fileshare
) - Storage Gateway (
<system name>-<environment name>-gateway
) - Storege Gatewayファイル共有
が作成されます。
ここからTerraformの各種リソースについて簡単に解説します。
a. ファイル共有用 IAM Role
Storage Gatewayファイル共有ではS3アクセスのためのIAM Roleが必要となります。
以下のドキュメントに従いS3バケットへのアクセス権を持つIAM Roleを用意します。
このIAM Roleは後述のaws_storagegateway_smb_file_share
リソースで使用します。
b. aws_storagegateway_gateway リソース
Storage Gatewayはaws_storagegateway_smb_file_shareリソースで定義します。
基本的なパラメーターはドキュメントをみてください。
アクティベーションのためにgateway_ip_address
かactivation_key
パラメーターのどちらかを指定する必要があります。
GatewayをPublic Subnetに配備しTerraformの実行環境からHTTPアクセス可能であればgateway_ip_address
を指定できます。
今回はPrivate Subnetに配備してるため事前に取得したactivation_key
を使う形としています。
あとはSMBに関わる設定もGateway本体で行います。
今回はゲストアクセスにしているのでsmb_guest_password
を指定してやります。
c. aws_storagegateway_cache リソース
Storage Gatewayのキャッシュ設定はaws_storagegateway_cacheリソースで行います。
設定自体はキャッシュで使うEBSボリュームを指定してやるだけなのですが、EBSボリュームをディスクID(disk_id
)で指定してやる必要があります。
EC2作成時のデバイス名(/dev/sdf
など)からIDを取得するためにaws_storagegateway_local_diskDataリソースを使ってやる必要があるのが軽いハマりポイントです。
// ディスクIDの取得のために aws_storagegateway_local_disk を使う
// 「disk_node」を以下の様に記載する
data "aws_storagegateway_local_disk" "sgw" {
gateway_arn = aws_storagegateway_gateway.sgw.arn
disk_node = "/dev/sdf"
}
resource "aws_storagegateway_cache" "sgw" {
gateway_arn = aws_storagegateway_gateway.sgw.arn
// aws_storagegateway_local_diskからディスクIDを設定
disk_id = data.aws_storagegateway_local_disk.sgw.id
}
d. aws_storagegateway_smb_file_share リソース
SMBによるファイル共有はaws_storagegateway_smb_file_shareリソースで定義します。
このリソースで使用するS3(location_arn
)や先述のIAM Role(role_arn
)等を定義します。
細かいパラメーターに関してはドキュメントを参照してください。
3. 動作確認
今回の例では\\10.0.21.125\share
が共有フォルダとなります。
このフォルダに対しゲストユーザーsmbguest
でアクセスしてやります。
ちゃんとS3に同期されています。
最後に
以上となります。
日本語情報があまりなかった様なので使用したtfファイルを雑に公開してみました。
Storage Gatewayが必要になるケース自体がそこまで多くないかもしれませんがこの記事が誰かの役に立てば幸いです。